﻿//  以下のサイトのソースコードを利用しています
//  http://www.codeproject.com/Articles/34390/Search-Result-Clustering-with-Non-Negative-Matrix
//
//  CPOLライセンス
//  http://www.codeproject.com/info/cpol10.aspx
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloudStorageLight.Core.Recommender
{
    [Serializable]
    public class RecommendTermModel
    {
        Dictionary<string, int> _termToIndex = new Dictionary<string, int>();
        Dictionary<int, string> _indexToTerm = new Dictionary<int, string>();

        [Serializable]
        public class Item
        {
            public Dictionary<int, uint> termCount = new Dictionary<int, uint>();
            public int index;
            public uint totalTermCount = 0;
            public long documentId;
            public Item(int index, long id) { this.index = index; this.documentId = id; }
        }

        public List<long> DocumentList
        {
            get { return _documentList.Select(x => x.documentId).ToList(); }
        }
        List<Item> _documentList = new List<Item>();


        public Dictionary<int, Dictionary<Item, bool>> KeywrordDocumentOccurence
        {
            get { return _keywordDocumentOccurence; }
        }
        Dictionary<int, Dictionary<Item, bool>> _keywordDocumentOccurence = new Dictionary<int, Dictionary<Item, bool>>();

        public RecommendTermModel()
        {
        }

        public Item AddRecommend(long id)
        {
            Item ret = new Item(_documentList.Count, id);
            _documentList.Add(ret);
            return ret;
        }
        public void AddTerm(Item doc, string term, uint count)
        {
            int index;
            if (!_termToIndex.TryGetValue(term, out index))
            {
                index = _termToIndex.Count;
                _termToIndex.Add(term, index);
                _indexToTerm.Add(index, term);
            }
            doc.termCount.Add(index, count);
            doc.totalTermCount += count;

            Dictionary<Item, bool> termBag;
            if (!_keywordDocumentOccurence.TryGetValue(index, out termBag))
                _keywordDocumentOccurence[index] = termBag = new Dictionary<Item, bool>();
            termBag[doc] = true;
        }
        public NNMFMatrix GetNormalised()
        {
            int numDocs = _documentList.Count;
            int numTerms = _termToIndex.Count;
            double nd = numDocs;
            NNMFMatrix ret = new NNMFMatrix(numTerms, numDocs);
            int x = 0;
            foreach (Item doc in _documentList)
            {
                double docTermCount = doc.totalTermCount;
                foreach (KeyValuePair<int, uint> item in doc.termCount)
                {
                    int termIndex = item.Key;
                    double count = item.Value;
                    double termFrequency = count / docTermCount;
                    double inverseDocumentFrequency = Math.Log(nd / _keywordDocumentOccurence[termIndex].Count);
                    double weight = inverseDocumentFrequency * termFrequency;
                    ret.Set(termIndex, x, (float)weight);
                }
                ++x;
            }
            return ret;
        }
        public Item GetDocument(int index)
        {
            return _documentList[index];
        }
        public string GetTerm(int index)
        {
            return _indexToTerm[index];
        }
        public int GetRecommendIndex(long id)
        {
            for (int i = 0; i < _documentList.Count; i++)
            {
                if (_documentList[i].documentId == id) return i;
            }
            return -1;
        }

        public int GetTermIndex(string term)
        {
            if (!_termToIndex.ContainsKey(term)) return -1;
            return _termToIndex[term];
        }

    }

}
